﻿#include "xserver_http.h"
#include <string.h>
#include <stdio.h>

#define XHTTP_DOC_DIR "E:\\miniTCP\\minNet\\htdocs" // html文档所在的目录
static char url_path[255], file_path[255];

#define XTCP_FIFO_SIZE 40

#define TX_BUFFER_SIZE 1024
#define RX_BUFFER_SIZE 1024
static uint8_t tx_buffer[TX_BUFFER_SIZE], rx_buffer[RX_BUFFER_SIZE];

static xnet_err_t chat_handler(xtcp_t *tcp, xtcp_conn_state_t state)
{
    static char *num = "0123456789ABCDEF";
    if (state == XTCP_CONN_CONNECTED)
    {
        printf("http connected\n");
        // 已连接就释放
        // tcp_close(tcp);

        // for (int i = 0; i < sizeof(tx_buffer); i++)
        // {
        //     tx_buffer[i] = num[i % 16];
        // }
        // uint16_t send_size = tcp_buffer_write(&tcp->tx_buf,tx_buffer , sizeof(tx_buffer));
        // if(send_size){
        //     tcp_send(tcp,XTCP_FLAG_ACK);
        // }
    }
    else if (state == XTCP_CONN_DATA_RECV)
    {
        uint8_t *data = tx_buffer;

        uint16_t read_data = tcp_buffer_read(&tcp->rx_buf, data, TX_BUFFER_SIZE);
        data[read_data] = '\0';
        printf("he : %s\n", data);
        printf("you : ");
        scanf("%s", rx_buffer);
        uint16_t send_size = tcp_buffer_write(&tcp->tx_buf, rx_buffer, sizeof(rx_buffer));
        if (send_size)
        {
            tcp_send(tcp, XTCP_FLAG_ACK);
        }
    }
    else if (state == XTCP_CONN_CLOSED)
    {
        printf("http closed \n");
    }
    return XNET_ERR_OK;
}

xnet_err_t xserver_tcp_chat_create(uint16_t port)
{

    xtcp_t *tcp = xtcp_open(chat_handler);
    xtcp_bind(tcp, port);
    xtcp_listen(tcp);
    return XNET_ERR_OK;
};

// http处理的循环队列
typedef struct _xhttp_fifo_t
{
    xtcp_t *buffer[XTCP_FIFO_SIZE];
    uint8_t front, tail; // 头和尾
    uint8_t count;
} xhttp_fifo_t;

static void xhttp_fifo_init(xhttp_fifo_t *fifo)
{
    fifo->count = 0;
    fifo->front = fifo->tail = 0;
}
// 入队列
static xnet_err_t xhttp_fifo_in(xhttp_fifo_t *fifo, xtcp_t *xtcp)
{
    if (fifo->count >= XTCP_FIFO_SIZE)
    {
        return XNET_ERR_MEM;
    }

    fifo->buffer[fifo->front++] = xtcp;

    if (fifo->front >= XTCP_FIFO_SIZE)
    {
        fifo->front = 0;
    }
    fifo->count++;
    return XNET_ERR_OK;
}

// 出队列
static xtcp_t *xhttp_fifo_out(xhttp_fifo_t *fifo)
{
    if (fifo->count == 0)
    {
        return (xtcp_t *)0; // 内部无数据
    }

    xtcp_t *tcp = fifo->buffer[fifo->tail++];

    if (fifo->tail >= XTCP_FIFO_SIZE)
    {
        fifo->tail = 0;
    }
    fifo->count--;
    return tcp;
}

static xhttp_fifo_t http_fifo;

// http句柄，将tcp的请求入队，然后交给xserver_http_run处理
static xnet_err_t http_handler(xtcp_t *tcp, xtcp_conn_state_t state)
{
    if (state == XTCP_CONN_CONNECTED)
    {
        xhttp_fifo_in(&http_fifo, tcp);
        printf("http conntected.\n");
    }
    else if (state == XTCP_CONN_CLOSED)
    {
        printf("http closed.\n");
    }
    return XNET_ERR_OK;
}

xnet_err_t xserver_http_create(uint16_t port)
{
    xnet_err_t err;

    xtcp_t *tcp = xtcp_open(http_handler);
    if (!tcp)
        return XNET_ERR_MEM;
    err = xtcp_bind(tcp, port); // HTTP熟知端口
    if (err < 0)
        return err;

    xhttp_fifo_init(&http_fifo);
    return xtcp_listen(tcp);
}

// 从tcp缓冲区里读取size个字符，遇到\n结束，不读取\r
static int get_line(xtcp_t *tcp, char *buf, int size)
{
    int i = 0;

    while (i < size)
    {
        char c;

        if (tcp_buffer_read(&tcp->rx_buf, (uint8_t *)&c, 1) > 0)
        {
            if ((c != '\n') && (c != '\r'))
            {
                buf[i++] = c;
            }
            else if (c == '\n')
            {
                break;
            }
        }
        xnet_poll();
    }
    buf[i] = '\0';
    return i;
}

/**
 * 向tcp发送数据
 */
int xtcp_write(xtcp_t *tcp, uint8_t *data, uint16_t size)
{
    uint16_t send_size;

    if ((tcp->state != XTCP_STATE_ESTABLISHED))
    {
        return -1;
    }

    send_size = tcp_buffer_write(&tcp->tx_buf, data, size);
    if (send_size)
    {
        tcp_send(tcp, XTCP_FLAG_ACK);
    }
    return send_size;
}

static int http_send(xtcp_t *tcp, char *buf, int size)
{
    int sended_size = 0;

    while (size > 0)
    {
        int curr_size = xtcp_write(tcp, (uint8_t *)buf, (uint16_t)size);
        if (curr_size <= 0)
            break;
        size -= curr_size;
        buf += curr_size;
        sended_size += curr_size;

        xnet_poll();
    }
    return sended_size;
}

// 发送文件
static void send_file(xtcp_t *tcp, const char *url)
{
    FILE *file;
    uint32_t size;
    const char *content_type = "text/html";
    int i;

    while (*url == '/')
        url++;
    sprintf(file_path, "%s\\%s", XHTTP_DOC_DIR, url);

    file = fopen(file_path, "rb");
    if (file == NULL)
    {
        //send_404_not_found(tcp);
        return;
    }

    fseek(file, 0, SEEK_END);
    size = ftell(file);
    fseek(file, 0, SEEK_SET);
    sprintf(tx_buffer,
            "HTTP/1.0 200 OK\r\n"
            "Content-Length:%d\r\n\r\n",
            (int)size);
    http_send(tcp, tx_buffer, strlen(tx_buffer));

    while (!feof(file))
    {
        size = fread(tx_buffer, 1, sizeof(tx_buffer), file);
        if (http_send(tcp, tx_buffer, size) <= 0)
        {
            fclose(file);
            return;
        }
    }
    fclose(file);
}

static void close_http(xtcp_t *tcp)
{
    xtcp_close(tcp);
    printf("http closed.\n");
}

void xserver_http_run(void)
{
    xtcp_t *tcp;

    while ((tcp = xhttp_fifo_out(&http_fifo)) != (xtcp_t *)0)
    {
        int i;
        char *c = rx_buffer;

        if (get_line(tcp, rx_buffer, sizeof(rx_buffer)) <= 0)
        {
            close_http(tcp);
            continue;
        }

        while (*c == ' ')
            c++; // 跳过空格
        if (strncmp(rx_buffer, "GET", 3) != 0)
        {
            // send_400_bad_request(tcp);
            close_http(tcp);
            continue;
        }

        while (*c != ' ')
            c++; // 跳过GET字符
        while (*c == ' ')
            c++; // 跳过空格
        for (i = 0; i < sizeof(url_path); i++)
        {
            if (*c == ' ')
                break;
            url_path[i] = *c++;
        }

        url_path[i] = '\0';
        if (url_path[strlen(url_path) - 1] == '/')
        {
            strcat(url_path, "index.html");
        }

        // 发送文件
        send_file(tcp, url_path);

        // 关掉服务器
        close_http(tcp);
    }
}
